//
//  Reuseable.swift
//  ADKit
//
//  Created by Adoma on 2023/12/27.
//

import UIKit
import RxSwift
import RxCocoa

// MARK: - NibLoadableView
public protocol NibLoadableView: UIView {}
public extension NibLoadableView {
    
    static var nibName: String {
        let clsName = NSStringFromClass(self)
        return clsName.components(separatedBy: ".").last!
    }
    
    static var nib: UINib? {
        let bundle = Bundle(for: self)
        let nibName = nibName
        if let _ = bundle.path(forResource: nibName, ofType: "nib") {
            return UINib(nibName: nibName, bundle: bundle)
        }
        return nil
    }
    
    static func loadFromNib(_ owner: AnyObject? = nil) throws -> Self {
        guard let view = nib?.instantiate(withOwner: owner, options: nil).first as? Self else {
            throw ADError.error(message: "\(self) nib instantiate failed!")
        }
        return view
    }
}

// MARK: Extension Cell
extension UITableViewCell: NibLoadableView {}
extension UITableViewHeaderFooterView: NibLoadableView {}
extension UICollectionReusableView: NibLoadableView {}

// MARK: Register Cell
public extension UITableView {
    
    ///Register cell
    func register<Cell: UITableViewCell>(_: Cell.Type) {
        if let nib = Cell.nib {
            self.register(nib, forCellReuseIdentifier: Cell.nibName)
        } else {
            self.register(Cell.self, forCellReuseIdentifier: Cell.nibName)
        }
    }
    
    ///Dequeue Cell
    func dequeueReusableCell<Cell: UITableViewCell>(forIndexPath indexPath: IndexPath) -> Cell {
        let identifier = Cell.nibName
        let cell = self.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
        return cell as! Cell
    }
    
    ///Register HeaderFooterView
    func registerHeaderFooter<View: UITableViewHeaderFooterView>(_: View.Type) {
        if let nib = View.nib {
            self.register(nib, forHeaderFooterViewReuseIdentifier: View.nibName)
        } else {
            self.register(View.self, forHeaderFooterViewReuseIdentifier: View.nibName)
        }
    }
    
    ///Dequeue HeaderFooterView
    func dequeueHeaderFooterView<View: UITableViewHeaderFooterView>(_: View.Type) -> View {
        let view = self.dequeueReusableHeaderFooterView(withIdentifier: View.nibName)
        return view as! View
    }
    
}

public extension UICollectionView {
    
    enum Kind {
        
        case header, footer
        
        var elementKind: String {
            switch self {
            case .header:
                return UICollectionView.elementKindSectionHeader
            default:
                return UICollectionView.elementKindSectionFooter
            }
        }
    }
    
    ///Register cell
    func register<Cell: UICollectionViewCell>(_: Cell.Type) {
        if let nib = Cell.nib {
            self.register(nib, forCellWithReuseIdentifier: Cell.nibName)
        } else {
            self.register(Cell.self, forCellWithReuseIdentifier: Cell.nibName)
        }
    }
    
    ///Dequeue Cell
    func dequeueReusableCell<Cell: UICollectionViewCell>(forIndexPath indexPath: IndexPath) -> Cell {
        let cell = self.dequeueReusableCell(withReuseIdentifier: Cell.nibName, for: indexPath)
        return cell as! Cell
    }
    
    ///Register SupplementaryView
    func register<View: UICollectionReusableView>(_: View.Type, kind: Kind) {
        if let nib = View.nib {
            self.register(nib, forSupplementaryViewOfKind: kind.elementKind, withReuseIdentifier: View.nibName)
        } else {
            self.register(View.self, forSupplementaryViewOfKind: kind.elementKind, withReuseIdentifier: View.nibName)
        }
    }
    
    ///Dequeue SupplementaryView
    func dequeueSupplementaryViewOfKind<View: UICollectionReusableView>(_ elementKind: String, forIndexPath: IndexPath) -> View {
        let view = self.dequeueReusableSupplementaryView(ofKind: elementKind, withReuseIdentifier: View.nibName, for: forIndexPath) as! View
        return view
    }
    
}

// MARK: Reactive Cell
extension Reactive where Base: UICollectionView {
    public func items<S: Sequence, Cell: UICollectionViewCell, O: ObservableType>(
        _ reusableCell: Cell.Type
    ) -> (_ source: O)
    -> (_ configureCell: @escaping (Int, S.Iterator.Element, Cell) -> Void)
    -> Disposable
    where O.Element == S {
        return { source in
            return { configureCell in
                return self.items(cellIdentifier: reusableCell.nibName, cellType: Cell.self)(source)(configureCell)
            }
        }
    }
}

extension Reactive where Base: UITableView {
    public func items<S: Sequence, Cell: UITableViewCell, O: ObservableType>(
        _ reusableCell: Cell.Type
    ) -> (_ source: O)
    -> (_ configureCell: @escaping (Int, S.Iterator.Element, Cell) -> Void)
    -> Disposable
    where O.Element == S {
        return { source in
            return { configureCell in
                return self.items(cellIdentifier: reusableCell.nibName, cellType: Cell.self)(source)(configureCell)
            }
        }
    }
}
